/**
 *  Class Name : EgovFileMntrg.java
 *  Description : 시스템 네트워크 정보를 확인하여 제공하는  Business class
 *  Modification Information
 * 
 *     수정일         수정자                   수정내용
 *   -------    --------    ---------------------------
 *   2009.01.13    조재영          최초 생성
 *
 *  @author 공통 서비스 개발팀 조재영
 *  @since 2009. 01. 13
 *  @version 1.0
 *  @see 
 * 
 *  Copyright (C) 2009 by EGOV  All right reserved.
 */
package egovframework.com.utl.sim.service;
import java.io.BufferedReader;
import java.io.FileReader;
import java.io.IOException;
import java.io.FileWriter;
import java.io.BufferedWriter;
import java.io.StringReader;
import java.io.File;
import java.util.ArrayList;

public class EgovFileMntrg extends Thread {

    /**
     * <p>
     * 해당 파일의 변경 유무를 체크하기 위한 Default 초의 stati final 변수, 기본 적용은 값은 60초
     * </p>
     */
    //static final public long DEFAULT_DELAY = 60000; // 60초
    static final public long DEFAULT_DELAY = 30000; // 30초
    
    /**
     * 최대 문자길이
     **/
    static final int MAX_STR_LEN = 1024;
    
    /**
     * <p>
     * 파일의 변경 유무를 체크하기 위한 해당파일명 변수
     * </p>
     */
    protected String filename;

    /**
     * <p>
     * 해당 파일의 변경 유무를 체크하기 위한 Default 초의 stati final 변수, 기본 적용은 값은 60초{@link
     * #DEFAULT_DELAY}.
     * </p>
     */
    protected long delay = DEFAULT_DELAY;

    File file;      // 타겟(감시대상) 디렉토리
    File logFile;   // 감시정보보관용 로그파일
    long lastModif = 0;
    boolean warnedAlready = false;
    boolean interrupted = false;
    ArrayList realOriginalList = new ArrayList(); // 최초의 원본리스트
    ArrayList originalList = new ArrayList();     // 직전리스트는 주기적으로 직전목록정보로 갱신된다.
    ArrayList currentList  = new ArrayList();     // 직전리스트와 비교할 현시점 리스트
    ArrayList changedList  = new ArrayList();     // 직전리스트와 비교한 시점에 발생된 변경리스트
    ArrayList totalChangedList = new ArrayList(); // 최초리스트와 비교한 변경 리스트
                                                  // totalChangedList는 필요시 checkAndConfigure함수 내에서 주석해제후 사용(부하량을 고려하여 사용)
    int cnt = 0;

    /**
     * <p>
     * 감시 하고자 하는 파일명을 파라메타로 받는 기본 컨스트럭터(Constructor).
     * </p>
     *
     * @param filename
     */
    protected EgovFileMntrg(String filename ,File logFile) {
    	//log.debug("EgovFileMntrg start");
        this.logFile = logFile;
    	this.filename = filename;
        file = new File(filename);
        // 1. 최초생성시 현재 디렉토리의 하위정보를 ArrayList에 보관한다. 보관정보 ==>  절대경로 + "," + 최종수정일시 + "," + 사이즈
        File [] fList = file.listFiles();
        for(int i = 0; i<fList.length; i++ ){
            realOriginalList.add(fList[i].getAbsolutePath() + "$"
            		+ getLastModifiedTime(fList[i]) + "$"
            		+ ((fList[i].length()/1024)>0?(fList[i].length()/1024):1) + "KB");
            writeLog("ORI_"+fList[i].getAbsolutePath() + "$" 
            		+ getLastModifiedTime(fList[i]) + "$" 
            		+ ((fList[i].length()/1024)>0?(fList[i].length()/1024):1) + "KB");
        }
        originalList = new ArrayList(realOriginalList);
        writeLog("START");
        setDaemon(true);
        checkAndConfigure();
        //log.debug("EgovFileMntrg end");
    }

    /**
     * <p>
     * 감시 하고자 하는 파일의 변경 유무를 체크 하고자 하는 delay 초를 set.
     * </p>
     *
     * @param delay 감시 주기 초
     */
    public void setDelay(long delay) {
        this.delay = delay;
    }

    /**
     * <p>
     * 해당 파일의 변경시 작업 할 내용을 기술 할 추상(abstract) 메소드
     * </p>
     */
    //abstract protected void doOnChange();
    protected void doOnChange(ArrayList changedList){
    	//log.debug("doOnChange() start");
    	for(int i = 0; i<changedList.size(); i++ ){
    		writeLog((String)changedList.get(i));
        }
    	changedList.clear();                         //직전리스트와 비교해서 변경된 내역은 로그처리한 후 초기화한다.
    	originalList = new ArrayList(currentList);   //현재리스트가 직전리스트가 된다.(새로 생성해야 함!)
        cnt++;
        
        //log.debug("doOnChange() end"); 
    }

    /**
     * <p>
     * 파일의 변경 유무를 체크하는 메소드
     * </p>
     */
    protected void checkAndConfigure() {
    	//log.debug("checkAndConfigure start");
        try {
        	currentList.clear();
            file = new File(filename);
        	// 현재정보를 ArrayList에 담는다.
            File[] fList = file.listFiles();
            for(int i = 0; i<fList.length; i++ ){
                currentList.add(fList[i].getAbsolutePath() + "$"
                		+ getLastModifiedTime(fList[i]) + "$"
                		+ ((fList[i].length()/1024)>0?(fList[i].length()/1024):1) + "KB");
            }
            /*
            for(int i = 0; i<originalList.size(); i++ ){
                //log.debug("in checkAndConfigure() ::: originalList:" + originalList.get(i));
            }
        	for(int i = 0; i<currentList.size(); i++ ){
                //log.debug("in checkAndConfigure() ::: currentList:" + currentList.get(i));
            }
        	*/
            boolean isSame = false;
            boolean isNew  = true;
            boolean isDel = true;
            String  str1   = "";
            String  str2   = "";
            int tmpCnt = 0;
            
            // 현재하위디렉토리정보와 초최하위디렉토리 정보를 비교한다. 삭제된 경우를 확인함
            for(int i = 0; i < originalList.size() ; i++){
            	for(int j = 0; j < currentList.size(); j++){
                    str1 = (String)originalList.get(i);
                    str2 = (String)currentList.get(j);
                    if(str1.substring(0, str1.indexOf("$")).equals( str2.substring(0, str2.indexOf("$"))) ){
                    	isDel = false;
                    }
                }
                if(isDel){
                	changedList.add("DEL$"+originalList.get(i));
                }
                isDel = true;  // 초기화
            }
            
            // 현재하위디렉토리 정보와 최초하위디렉토리 정보를 비교한다.(신규로 생성되었거나 수정된 경우를 확인함)
            for(int i = 0; i < currentList.size() ; i++){
            	for(int j = 0; j < originalList.size(); j++){
                    if(((String)currentList.get(i)).equals((String)originalList.get(j))){
                    	isSame    = true;
                    }
                    str1 = (String)currentList.get(i);
                    str2 = (String)originalList.get(j);
                    if(str1.substring(0, str1.indexOf("$")).equals( str2.substring(0, str2.indexOf("$"))) ){
                    	isNew = false;
                    }
                }
                if(!isSame){
                    if(isNew){
                    	changedList.add("NEW$"+currentList.get(i));
                        //totalChangedList.add("NEW$"+currentList.get(i));	
                    }else{
                    	changedList.add("MODI$"+currentList.get(i));
                        //totalChangedList.add("MODI$"+currentList.get(i));
                    }
                }
                isSame = false; // 초기화
                isNew  = true;  // 초기화
            }
            
        } catch (Exception e) {
            //interrupted = true; // there is no point in continuing
            e.printStackTrace();
            //return;
        }

        if (changedList.size()>0) {
        	//log.debug("change occur , changed file check count:"+cnt+ " , changed file count:"+changedList.size());
        	doOnChange(changedList);
        }
        
        if (isEnd()){
        	//log.debug("Thread Process END !!! (CNT :"+cnt+")");
            interrupted = true;
        }
        //log.debug("checkAndConfigure end"+changedList.size());
    }

    /**
     * <p>
     * 파일의 변경 유무의 체크를 주기적 초 단위로 실행 시키는 메소드
     * </p>
     */
    public void run() {
        while (!interrupted) {
            try {
                Thread.sleep(delay);
            } catch (InterruptedException e) {
                // no interruption expected
            }
            checkAndConfigure();
        }
        if(interrupted){
        	//log.debug(filename+" Monitering Thread is interrupted");
        	this.interrupt();
        }
    }
    
    /**
     * <pre>
     * Comment : 디렉토리(파일)의 최종 수정시간를 확인한다.(기본로케일 java.util.Locale.KOREA 기준)
     * </pre>
     * @param File f 수정일자를 확인할 대상파일
     * @return String result 최종수정일자를 문자열로 리턴한다.
     */
    public static String getLastModifiedTime(File f){
		long date  = f.lastModified();
		java.text.SimpleDateFormat dateFormat = new java.text.SimpleDateFormat("yyyyMMdd:HH:mm:ss",
																			    java.util.Locale.KOREA);
		return dateFormat.format(new java.util.Date(date));
	}
    
    /**
     * <pre>
     * Comment : 디렉토리(파일)의  로그정보를 기록한다.
     * </pre>
     * @param String  logStr  추가할 로그정보(라인단위)
     * @return boolean result  로그추가 성공여부  
     */
    public  boolean writeLog(String logStr){
		boolean result = false;
		
		try{	
			FileWriter fWriter = new FileWriter(logFile, true);
			BufferedWriter bWriter = new BufferedWriter(fWriter);
			BufferedReader br = new BufferedReader(new StringReader(logStr));
	        String line = "";
	        while((line = br.readLine()) != null){
	        	if (line.length() <= MAX_STR_LEN){
	        	    bWriter.write(line+"\n", 0 , line.length()+1);
	        	}
	        }
	        br.close();
			bWriter.close();
			fWriter.close();
			result = true;
		}catch(IOException ioe){
			ioe.printStackTrace();
		}catch(Exception e){
			e.printStackTrace();
		}
	
		return result;
	}
    
 
    
    /**
     * <pre>
     * Comment : 디렉토리감시 종료여부를 확인한다. 해당 디렉토리에 대한 로그파일이 삭제된 경우는 감시를 종료한다.
     * </pre>
     * @return boolean isEnd 감시종료여부 중단하려면  true 리턴, 계속하려면 false 리턴  
     */
    public  boolean isEnd(){
    	//log.debug("isEnd start");
    	boolean isEnd = false;
    	String lastStr = "";
    	
		try{	
			if(logFile.exists()){
				//로그파일을 읽어서 마지막 끝에 END가 있으면 종료된것임
				
				FileReader fr = new FileReader(logFile);
				BufferedReader br = new BufferedReader (fr);
	        	int ch = 0;
	        	String line = "";
	        	while((line = br.readLine()) != null){
	        		if (line.length() <= MAX_STR_LEN){
	        		    lastStr = line;
	        		}
	        	}
	            if(lastStr.equals("END")){
	            	isEnd=true;
	            }
			}else{
				//로그파일이 없는 경우(삭제된 경우)도 종료한다.
				isEnd = true;
			}
		}catch(Exception e){
			e.printStackTrace();
		}
		//log.debug("isEnd end"+isEnd);
		return isEnd;
	}
}

